/*
 * main.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "symbol.h"
#include "absyn.h"
#include "errormsg.h"
#include "temp.h" /* needed by translate.h */
#include "tree.h" /* needed by frame.h */
#include "assem.h"
#include "frame.h" /* needed by translate.h and printfrags prototype */
#include "semant.h" /* function prototype for transProg */
#include "canon.h"
#include "escape.h"
#include "parse.h"
#include "codegen.h"
#include "graph.h"
#include "flowgraph.h"
#include "liveness.h"
#include "color.h"
#include "regalloc.h"

extern bool anyErrors;

/* print the assembly language instructions to filename.s */
static void doProc(FILE *out, F_frame frame, T_stm body) {
	AS_proc proc;
	struct RA_result allocation;
	T_stmList stmList;
	AS_instrList iList;

	stmList = C_linearize(body);
	stmList = C_traceSchedule(C_basicBlocks(stmList));
	//printStmList(stdout, stmList);

	iList  = F_codegen(frame, stmList); /* 9 */
	allocation = RA_regAlloc(frame, iList);
	proc = F_procEntryExit3(frame, allocation.il);
	fprintf(out, "%s\n", proc->prolog);
	AS_printInstrList(out, proc->body, allocation.coloring);
	fprintf(out, "%s\n\n", proc->epilog);
}

int main(int argc, string *argv) {
	A_exp absyn_root;
	F_fragList frags;
	char outfile[100];
	char *input;
	FILE *out;
	

	if(argc < 2) {
		printf("a.out: fatal error: no input files\n");
		return 1;
	}
	if(!strcmp(argv[1], "-help")) {
		printf("Usage: a.out [options] tiger file.tig\n");
		printf("Options:\n");
		printf("  -help\t\t\t\tDisplay the information\n");
		printf("  -dumpmachine\t\t\t\tDisplay the compiler's target processor\n");
		printf("  -S\t\t\t\tCompile only; do not assemble or link\n");
		return 0;
	}
	else if(!strcmp(argv[1], "-dumpmachine")) {
		printf("%s\n", F_archName);
		return 0;
	}
	else if(!strcmp(argv[1], "-S")) {
		if(argc < 3) {
			printf("a.out: fatal error: no input files\n");
			return 1;
		}
		input = argv[2];
	}
	else
		input = argv[1];
	
	absyn_root = parse(input);
	if (!absyn_root)
		return 1;

	Esc_findEscape(absyn_root); /* set varDec's escape field */
	frags = SEM_transProg(absyn_root);
	if (anyErrors) 
		return 1; /* don't continue */

	/* convert the filename */
	sprintf(outfile, "%s.s", input);
	out = fopen(outfile, "w");
	/* Chapter 8, 9, 10, 11 & 12 */
	for (;frags;frags=frags->tail) {
		if (frags->head->kind == F_procFrag) 
			doProc(out, frags->head->u.proc.frame, frags->head->u.proc.body);
		else if (frags->head->kind == F_stringFrag) 
			fprintf(out, "%s\n\n", F_String(frags->head->u.stringg.label, frags->head->u.stringg.str));
	}
	fclose(out);
	if(!strcmp(argv[1], "-S"))
		return 0; // has complished the goal of compiling successfully
	// otherwise we should link the assembly with corresponding runtime file 
	// additionally.
	char command[256];
	if(!strcmp(F_archName, "mips32")) {
		sprintf(command, "cat mipsruntime.s %s > %s.asm", outfile, input);
		// link the output and the mipsruntime
		if(system(command))
			return 1;
		sprintf(command, "rm %s", outfile);
		system(command);
	}
	else if(!strcmp(F_archName, "x64")) {
		sprintf(command, "gcc -o %s.o -c %s", input, outfile);
		// assemble the output
		if(system(command))
			return 1;
		// compile and assemble the x64runtime
		if(system("gcc -c x64runtime.c"))
			return 1;
		// link
		sprintf(command, "gcc -o %s.out %s.o x64runtime.o", input, input);
		if(system(command))
			return 1;
		// remove the assembly
		sprintf(command, "rm %s", outfile);
		system(command);
		// remove the object file generated by the assembly
		sprintf(command, "rm %s.o", input);
		system(command);
	}
	else {
		printf("a.out: fatal error: unknown architecture\n");
		return 1;
	}
	return 0;
}
